Flutter Mobile, Tablet & Desktop UI – Detailed Notes
Flutter allows developers to build applications for mobile, tablet, desktop, and web from a shared codebase. However, creating a good multi-device application does not mean simply stretching the same mobile interface onto a larger screen. The UI should respond to the available space and, when necessary, change its structure, navigation, content density, and interaction patterns.
Flutter's adaptive and responsive design guidance recommends making layout decisions based on the space available to the application rather than assuming that a particular physical device type always has a particular screen size. This is especially important for tablets, foldables, split-screen windows, desktop applications, and resizable web browsers.
1. What is Mobile, Tablet & Desktop UI?
Mobile, tablet, and desktop UI refers to designing a Flutter application so that its interface remains usable and visually appropriate across different screen sizes and input environments.
- Mobile UI: Designed primarily for compact screens and touch interaction.
- Tablet UI: Uses additional horizontal space while remaining touch-friendly.
- Desktop UI: Uses larger screens and commonly supports mouse, keyboard, hover, scrolling, and larger information layouts.
A Flutter application can use the same underlying data and business logic while presenting different UI structures for different available window sizes.
2. Why Multi-Device UI Design is Important
A Flutter application may run on many different screen sizes. A mobile layout that works well on a phone can look excessively stretched on a desktop monitor.
Multi-device UI design helps you:
- Prevent layout overflow.
- Use tablet space effectively.
- Use desktop space effectively.
- Create readable content on large screens.
- Support browser resizing.
- Support split-screen layouts.
- Provide appropriate navigation patterns.
- Support touch, mouse, keyboard, and trackpad interaction.
- Build reusable responsive components.
3. Responsive vs Adaptive UI
| Responsive UI |
Adaptive UI |
| Adjusts the placement and sizing of UI elements. |
Selects an appropriate UI structure for the available space. |
| Example: cards resize according to available width. |
Example: bottom navigation changes to side navigation. |
| Focuses on fitting the UI into available space. |
Focuses on keeping the UI usable in that space. |
| Often uses flexible widgets. |
Often uses layout branching and alternative widgets. |
In real Flutter applications, responsive and adaptive techniques are commonly used together.
4. Important Principle: Design for Available Space
Do not assume that a physical device category always determines the amount of space available to your application.
For example, a tablet can run an application in split-screen mode, giving the application a much narrower window than the full tablet display. Similarly, a desktop application can be resized to a small window.
Therefore, instead of writing:
if (isTablet) {
return TabletUI();
}
prefer a layout decision based on available width:
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth >= 600) {
return const LargeLayout();
}
return const CompactLayout();
},
)
5. Typical Layout Strategy
A common application strategy can be:
| Available Width |
Possible Layout |
| Small |
Single-column mobile layout. |
| Medium |
Two-column or tablet layout. |
| Large |
Sidebar + content layout. |
| Extra Large |
Sidebar + constrained content + additional panels. |
The exact breakpoints should be selected based on the application's design requirements. They should not be treated as universal device classifications.
6. Mobile UI Design
Mobile devices generally provide limited horizontal space. A mobile interface often benefits from a simple hierarchy, vertical scrolling, compact navigation, and touch-friendly controls.
Common Mobile UI Characteristics
- Single-column content.
- Bottom navigation.
- App bars.
- Large touch targets.
- Vertical lists.
- Compact forms.
- Scrollable content.
- Reduced visual density.
Example Mobile Layout
class MobileHome extends StatelessWidget {
const MobileHome({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Mobile Home'),
),
body: ListView(
padding: const EdgeInsets.all(16),
children: const [
Card(
child: ListTile(
leading: Icon(Icons.home),
title: Text('Home'),
),
),
Card(
child: ListTile(
leading: Icon(Icons.person),
title: Text('Profile'),
),
),
Card(
child: ListTile(
leading: Icon(Icons.settings),
title: Text('Settings'),
),
),
],
),
bottomNavigationBar: NavigationBar(
destinations: [
NavigationDestination(
icon: Icon(Icons.home),
label: 'Home',
),
NavigationDestination(
icon: Icon(Icons.person),
label: 'Profile',
),
],
),
);
}
}
7. Tablet UI Design
Tablets provide more horizontal space than typical mobile screens. Instead of simply enlarging mobile widgets, use the additional space to display more information at the same time.
Common Tablet UI Characteristics
- Two-column layouts.
- Multi-column grids.
- Navigation rail.
- Larger content areas.
- List-detail layouts.
- More dashboard cards.
- Side-by-side content.
Example Tablet Layout
class TabletHome extends StatelessWidget {
const TabletHome({super.key});
@override
Widget build(BuildContext context) {
return Row(
children: [
NavigationRail(
selectedIndex: 0,
onDestinationSelected: (index) {},
destinations: const [
NavigationRailDestination(
icon: Icon(Icons.home),
label: Text('Home'),
),
NavigationRailDestination(
icon: Icon(Icons.person),
label: Text('Profile'),
),
],
),
const Expanded(
child: Center(
child: Text('Tablet Content'),
),
),
],
);
}
}
8. Desktop UI Design
Desktop screens provide significantly more space and generally support mouse, keyboard, trackpad, hover, right-click, scrolling and keyboard shortcuts.
Common Desktop UI Characteristics
- Sidebar navigation.
- Navigation rail.
- Multiple panels.
- Data tables.
- Multi-column dashboards.
- Hover states.
- Keyboard shortcuts.
- Mouse and trackpad interaction.
- More visible controls.
- Constrained content areas.
Example Desktop Layout
class DesktopHome extends StatelessWidget {
const DesktopHome({super.key});
@override
Widget build(BuildContext context) {
return Row(
children: [
SizedBox(
width: 260,
child: Container(
color: Colors.black12,
child: const Center(
child: Text('Sidebar'),
),
),
),
const VerticalDivider(width: 1),
const Expanded(
child: Center(
child: Text('Desktop Content'),
),
),
],
);
}
}
9. Using LayoutBuilder
LayoutBuilder is one of the most useful widgets for creating mobile, tablet, and desktop layouts because it provides the constraints available from the parent widget.
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth < 600) {
return const MobileHome();
}
if (constraints.maxWidth < 1000) {
return const TabletHome();
}
return const DesktopHome();
},
)
The constraints.maxWidth value allows the application to choose an appropriate layout according to the available width.
10. Using MediaQuery
MediaQuery.sizeOf(context) provides the current size of the application's window/view in logical pixels.
final size = MediaQuery.sizeOf(context);
final width = size.width;
final height = size.height;
Example:
Widget build(BuildContext context) {
final width = MediaQuery.sizeOf(context).width;
if (width < 600) {
return const MobileHome();
}
return const DesktopHome();
}
Use MediaQuery when the overall application window size matters. Use LayoutBuilder when the local constraints available to a specific widget matter.
11. Mobile, Tablet and Desktop with LayoutBuilder
class AdaptiveHome extends StatelessWidget {
const AdaptiveHome({super.key});
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth < 600) {
return const MobileHome();
} else if (constraints.maxWidth < 1000) {
return const TabletHome();
} else {
return const DesktopHome();
}
},
);
}
}
This pattern keeps the layout decision in one location and allows each layout to be developed separately.
12. Mobile vs Tablet vs Desktop Navigation
| Platform / Size |
Possible Navigation |
| Mobile |
NavigationBar, Drawer or compact navigation. |
| Tablet |
NavigationRail or navigation with more visible destinations. |
| Desktop |
Sidebar, NavigationRail, menus and multi-panel navigation. |
For example:
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth < 600) {
return const MobileNavigation();
}
return const LargeScreenNavigation();
},
)
13. Mobile NavigationBar
NavigationBar(
selectedIndex: 0,
onDestinationSelected: (index) {},
destinations: const [
NavigationDestination(
icon: Icon(Icons.home),
label: 'Home',
),
NavigationDestination(
icon: Icon(Icons.search),
label: 'Search',
),
NavigationDestination(
icon: Icon(Icons.person),
label: 'Profile',
),
],
)
This type of navigation is useful when horizontal space is limited.
14. Tablet and Desktop NavigationRail
NavigationRail(
selectedIndex: 0,
onDestinationSelected: (index) {},
destinations: const [
NavigationRailDestination(
icon: Icon(Icons.home),
label: Text('Home'),
),
NavigationRailDestination(
icon: Icon(Icons.search),
label: Text('Search'),
),
NavigationRailDestination(
icon: Icon(Icons.person),
label: Text('Profile'),
),
],
)
A rail can make better use of horizontal space when the window is wide enough.
15. Adaptive List Layout
A mobile application may use a single vertical list:
ListView.builder(
itemCount: 20,
itemBuilder: (context, index) {
return ListTile(
title: Text('Item ${index + 1}'),
);
},
)
On a larger screen, the same data can be displayed in a grid:
GridView.builder(
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 250,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
),
itemCount: 20,
itemBuilder: (context, index) {
return Card(
child: Center(
child: Text('Item ${index + 1}'),
),
);
},
)
This allows content to use large screens without making each item unnecessarily wide.
16. Adaptive Product Grid
LayoutBuilder(
builder: (context, constraints) {
return GridView.builder(
padding: const EdgeInsets.all(16),
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 280,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
childAspectRatio: 0.8,
),
itemCount: 30,
itemBuilder: (context, index) {
return Card(
child: Column(
children: [
const Expanded(
child: Icon(
Icons.shopping_bag,
size: 60,
),
),
Text('Product ${index + 1}'),
const SizedBox(height: 10),
],
),
);
},
);
},
)
Using a maximum item extent allows the grid to adapt naturally as the window becomes wider.
17. Mobile, Tablet and Desktop Dashboard
A dashboard can display different structures depending on available space.
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth < 600) {
return ListView(
padding: const EdgeInsets.all(16),
children: const [
DashboardCard(title: 'Users'),
DashboardCard(title: 'Orders'),
DashboardCard(title: 'Revenue'),
DashboardCard(title: 'Pending'),
],
);
}
if (constraints.maxWidth < 1000) {
return GridView.count(
padding: const EdgeInsets.all(20),
crossAxisCount: 2,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
children: const [
DashboardCard(title: 'Users'),
DashboardCard(title: 'Orders'),
DashboardCard(title: 'Revenue'),
DashboardCard(title: 'Pending'),
],
);
}
return GridView.count(
padding: const EdgeInsets.all(24),
crossAxisCount: 4,
crossAxisSpacing: 20,
mainAxisSpacing: 20,
children: const [
DashboardCard(title: 'Users'),
DashboardCard(title: 'Orders'),
DashboardCard(title: 'Revenue'),
DashboardCard(title: 'Pending'),
],
);
},
)
class DashboardCard extends StatelessWidget {
final String title;
const DashboardCard({
super.key,
required this.title,
});
@override
Widget build(BuildContext context) {
return Card(
child: Center(
child: Text(
title,
style: const TextStyle(fontSize: 20),
),
),
);
}
}
18. Adaptive Forms
Forms should remain readable on both small and large screens. A desktop form should generally have a maximum width instead of expanding across the entire monitor.
LayoutBuilder(
builder: (context, constraints) {
final formWidth = constraints.maxWidth > 500
? 450.0
: constraints.maxWidth * 0.9;
return Center(
child: SizedBox(
width: formWidth,
child: Column(
children: [
const TextField(
decoration: InputDecoration(
labelText: 'Name',
),
),
const SizedBox(height: 16),
const TextField(
decoration: InputDecoration(
labelText: 'Email',
),
),
const SizedBox(height: 16),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {},
child: const Text('Submit'),
),
),
],
),
),
);
},
)
19. Adaptive Login Screen
class LoginPage extends StatelessWidget {
const LoginPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth < 700) {
return _mobileLogin();
}
return _largeLogin();
},
),
),
);
}
Widget _mobileLogin() {
return Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: _loginForm(),
),
);
}
Widget _largeLogin() {
return Row(
children: [
const Expanded(
child: ColoredBox(
color: Colors.black12,
child: Center(
child: Text('Welcome'),
),
),
),
Expanded(
child: Center(
child: SizedBox(
width: 420,
child: _loginForm(),
),
),
),
],
);
}
Widget _loginForm() {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const TextField(
decoration: InputDecoration(
labelText: 'Email',
),
),
const SizedBox(height: 16),
const TextField(
obscureText: true,
decoration: InputDecoration(
labelText: 'Password',
),
),
const SizedBox(height: 20),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {},
child: const Text('Login'),
),
),
],
);
}
}
20. Adaptive List-Detail UI
List-detail interfaces are particularly useful on tablets and desktops.
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth >= 700) {
return Row(
children: [
SizedBox(
width: 300,
child: ContactList(),
),
const VerticalDivider(width: 1),
const Expanded(
child: ContactDetails(),
),
],
);
}
return const ContactList();
},
)
On mobile, the application can navigate from the contact list to a separate details page. On larger screens, both areas can be displayed simultaneously.
21. Adaptive Sidebar
LayoutBuilder(
builder: (context, constraints) {
final isLarge = constraints.maxWidth >= 900;
if (!isLarge) {
return const MobileContent();
}
return Row(
children: [
const SizedBox(
width: 260,
child: Sidebar(),
),
const VerticalDivider(width: 1),
const Expanded(
child: MainContent(),
),
],
);
},
)
This approach allows the application to expose more navigation options when sufficient space exists.
22. Using Row and Column Adaptively
A common adaptive pattern is to use a Column for compact layouts and a Row for wider layouts.
LayoutBuilder(
builder: (context, constraints) {
final isWide = constraints.maxWidth >= 700;
if (isWide) {
return const Row(
children: [
Expanded(child: Text('Section 1')),
Expanded(child: Text('Section 2')),
],
);
}
return const Column(
children: [
Text('Section 1'),
Text('Section 2'),
],
);
},
)
23. Using Expanded and Flexible
Do not depend on fixed widths for every widget. Expanded and Flexible help distribute available space.
Row(
children: [
Expanded(
flex: 1,
child: Container(
height: 150,
child: const Center(
child: Text('Left'),
),
),
),
Expanded(
flex: 2,
child: Container(
height: 150,
child: const Center(
child: Text('Right'),
),
),
),
],
)
The second widget receives twice the flex space of the first widget.
24. Avoiding Excessive Horizontal Space
Large screens can tempt developers to stretch content across the entire width. However, very wide text fields, paragraphs and cards can reduce readability.
Use ConstrainedBox when appropriate:
Center(
child: ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 900,
),
child: const Padding(
padding: EdgeInsets.all(24),
child: Text(
'This content has a maximum readable width.',
),
),
),
)
25. SafeArea for Mobile and Tablet
Some mobile and tablet devices contain display cutouts, status bars, rounded corners, or other system UI areas. SafeArea helps keep content away from these intrusions.
Scaffold(
body: SafeArea(
child: ListView(
padding: const EdgeInsets.all(16),
children: const [
Text('Safe Content'),
],
),
),
)
SafeArea is especially useful for edge-to-edge mobile layouts.
26. Orientation Handling
Mobile devices can change between portrait and landscape. An adaptive UI should be able to handle both orientations when appropriate.
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth > constraints.maxHeight) {
return const WideLayout();
}
return const CompactLayout();
},
)
However, orientation alone should not be the main basis for layout decisions. Available width and height are more useful because a desktop or tablet window can be resized independently of physical orientation.
27. Supporting Mouse and Keyboard on Desktop
Desktop applications are not only larger versions of mobile applications. They can also provide additional input methods.
Desktop users commonly expect:
- Mouse clicks.
- Hover effects.
- Keyboard navigation.
- Keyboard shortcuts.
- Scroll wheel support.
- Trackpad gestures.
- Right-click interactions where appropriate.
Flutter's Material widgets provide many standard interactions automatically, while custom widgets may need additional handling.
28. Hover Effects for Desktop
class HoverCard extends StatefulWidget {
const HoverCard({super.key});
@override
State createState() => _HoverCardState();
}
class _HoverCardState extends State {
bool isHovered = false;
@override
Widget build(BuildContext context) {
return MouseRegion(
onEnter: (_) {
setState(() {
isHovered = true;
});
},
onExit: (_) {
setState(() {
isHovered = false;
});
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
),
child: Text(
isHovered ? 'Hovered' : 'Move Mouse Here',
),
),
);
}
}
29. Adaptive Tables
Tables can work well on desktop screens but may be difficult to use on narrow mobile screens.
On mobile, consider:
- Card-based layouts.
- Horizontally scrollable tables when appropriate.
- Showing fewer columns.
- Displaying important fields first.
On desktop, more columns can be visible at once.
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth < 700) {
return const MobileDataCards();
}
return const DesktopDataTable();
},
)
30. Adaptive Dialogs
A dialog that works well on a phone may become unnecessarily narrow or wide on a desktop.
LayoutBuilder(
builder: (context, constraints) {
final width = constraints.maxWidth > 600
? 500.0
: constraints.maxWidth * 0.9;
return Center(
child: SizedBox(
width: width,
child: const Card(
child: Padding(
padding: EdgeInsets.all(24),
child: Text('Adaptive Dialog Content'),
),
),
),
);
},
)
31. Adaptive Images
Images should scale without breaking their aspect ratio.
AspectRatio(
aspectRatio: 16 / 9,
child: Image.network(
'https://example.com/image.jpg',
fit: BoxFit.cover,
),
)
For responsive image layouts, avoid unnecessarily fixed image dimensions. Let the parent determine the available size where appropriate.
32. Adaptive Text
Text should remain readable across screen sizes and accessibility settings.
Text(
'Responsive Flutter UI',
style: Theme.of(context).textTheme.headlineMedium,
)
Prefer Flutter's text styles and flexible layouts instead of forcing large amounts of text into fixed-height containers.
Always test interfaces with larger text settings because accessibility text scaling can affect layout.
33. Adaptive App Bar
Large screens can expose additional actions while compact screens may need fewer visible actions.
LayoutBuilder(
builder: (context, constraints) {
return AppBar(
title: const Text('Dashboard'),
actions: constraints.maxWidth > 700
? [
TextButton(
onPressed: () {},
child: const Text('Reports'),
),
IconButton(
onPressed: () {},
icon: const Icon(Icons.settings),
),
]
: [
IconButton(
onPressed: () {},
icon: const Icon(Icons.more_vert),
),
],
);
},
)
34. Complete Mobile, Tablet & Desktop Example
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: const AdaptiveHomePage(),
);
}
}
class AdaptiveHomePage extends StatelessWidget {
const AdaptiveHomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Multi-Device Flutter UI'),
),
body: LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth < 600) {
return const MobileLayout();
}
if (constraints.maxWidth < 1000) {
return const TabletLayout();
}
return const DesktopLayout();
},
),
);
}
}
class MobileLayout extends StatelessWidget {
const MobileLayout({super.key});
@override
Widget build(BuildContext context) {
return ListView(
padding: const EdgeInsets.all(16),
children: const [
Card(
child: Padding(
padding: EdgeInsets.all(20),
child: Text('Mobile Dashboard'),
),
),
Card(
child: Padding(
padding: EdgeInsets.all(20),
child: Text('Users'),
),
),
Card(
child: Padding(
padding: EdgeInsets.all(20),
child: Text('Orders'),
),
),
],
);
}
}
class TabletLayout extends StatelessWidget {
const TabletLayout({super.key});
@override
Widget build(BuildContext context) {
return Row(
children: [
NavigationRail(
selectedIndex: 0,
onDestinationSelected: (index) {},
destinations: const [
NavigationRailDestination(
icon: Icon(Icons.home),
label: Text('Home'),
),
NavigationRailDestination(
icon: Icon(Icons.person),
label: Text('Users'),
),
],
),
const Expanded(
child: Padding(
padding: EdgeInsets.all(20),
child: Text('Tablet Dashboard'),
),
),
],
);
}
}
class DesktopLayout extends StatelessWidget {
const DesktopLayout({super.key});
@override
Widget build(BuildContext context) {
return Row(
children: [
const SizedBox(
width: 260,
child: ColoredBox(
color: Colors.black12,
child: Center(
child: Text('Desktop Sidebar'),
),
),
),
const VerticalDivider(width: 1),
Expanded(
child: GridView.count(
padding: const EdgeInsets.all(24),
crossAxisCount: 4,
crossAxisSpacing: 20,
mainAxisSpacing: 20,
children: const [
Card(child: Center(child: Text('Users'))),
Card(child: Center(child: Text('Orders'))),
Card(child: Center(child: Text('Revenue'))),
Card(child: Center(child: Text('Pending'))),
],
),
),
],
);
}
}
35. Recommended Project Structure
For a larger multi-device Flutter application, separate shared components from layout-specific screens.
lib/
├── main.dart
├── models/
│ └── product.dart
├── data/
│ └── product_data.dart
├── widgets/
│ ├── adaptive_layout.dart
│ ├── dashboard_card.dart
│ └── product_card.dart
├── layouts/
│ ├── mobile_layout.dart
│ ├── tablet_layout.dart
│ └── desktop_layout.dart
└── screens/
├── home_screen.dart
├── profile_screen.dart
└── settings_screen.dart
This structure can make adaptive applications easier to maintain.
36. Keeping Business Logic Shared
Different layouts should not duplicate application logic unnecessarily.
For example, the same product model can be used by mobile, tablet and desktop interfaces.
class Product {
final String name;
final double price;
const Product({
required this.name,
required this.price,
});
}
Only the presentation structure changes:
if (isMobile) {
return ProductList(products: products);
}
return ProductGrid(products: products);
This separation keeps the application easier to test and maintain.
37. State Preservation Across Layout Changes
When the window size or orientation changes, an adaptive application should preserve important state whenever possible.
Examples of state that may need to be preserved:
- Selected navigation item.
- Scroll position.
- Form input.
- Selected product.
- Expanded sections.
- Filters.
For scrollable widgets, PageStorageKey can help preserve scroll state in appropriate situations.
ListView(
key: const PageStorageKey('products'),
children: const [
Text('Product 1'),
Text('Product 2'),
Text('Product 3'),
],
)
38. Do Not Lock Orientation Unnecessarily
An adaptive application should ideally handle different window sizes and orientations instead of assuming that the application will always be portrait.
Modern environments can include:
- Landscape phones.
- Tablets in different orientations.
- Foldable devices.
- Split-screen applications.
- Desktop windows.
- Resizable browser windows.
39. Common Mistakes
Mistake 1: Designing Only for Mobile
A mobile UI may look stretched or empty on desktop.
Mistake 2: Using Device-Type Checks
if (isTablet) {
// Tablet layout
}
Prefer available window or local layout constraints.
Mistake 3: Hard-Coding Too Many Widths
Container(
width: 700,
)
This can cause overflow on smaller windows.
Mistake 4: Using Full Width on Desktop
Text fields and paragraphs can become excessively wide. Use maximum content widths where appropriate.
Mistake 5: Using Orientation as the Only Rule
Landscape does not automatically mean tablet or desktop.
Mistake 6: Ignoring Input Differences
Desktop users commonly expect keyboard, mouse, hover and scrolling interactions.
Mistake 7: Duplicating Business Logic
Keep data and business logic shared whenever possible and change only the UI structure.
40. Best Practices
- Design for available window size.
- Use
LayoutBuilder for local constraints.
- Use
MediaQuery.sizeOf for whole-window measurements.
- Use meaningful breakpoints.
- Avoid hardware-type checks for layout decisions.
- Use
Expanded and Flexible.
- Use
ConstrainedBox or maximum widths on large screens.
- Use adaptive navigation.
- Use grids to make effective use of large screens.
- Support mouse and keyboard input on desktop.
- Keep touch interactions comfortable on mobile and tablet.
- Use
SafeArea when appropriate.
- Preserve application state when layouts change.
- Break complex widgets into smaller reusable components.
- Test many different window sizes.
41. Testing Mobile UI
Test mobile layouts using:
- Small portrait screens.
- Large portrait screens.
- Landscape orientation.
- Different text sizes.
- Keyboard visibility.
- Long content.
- Different device aspect ratios.
Check for:
- Overflow.
- Clipped text.
- Unreachable buttons.
- Incorrect scrolling.
- Touch target problems.
42. Testing Tablet UI
Test tablets in:
- Portrait mode.
- Landscape mode.
- Split-screen mode.
- Different tablet widths.
- Different text scaling settings.
Check whether the additional horizontal space is actually being used effectively.
43. Testing Desktop UI
Test desktop applications with:
- Small windows.
- Medium windows.
- Large windows.
- Very wide windows.
- Mouse input.
- Keyboard input.
- Trackpad input.
- Hover interactions.
- Keyboard navigation.
Also test resizing the application continuously to identify layout problems.
44. Adaptive UI Checklist
- Does the mobile layout fit narrow screens?
- Does the tablet layout use additional space effectively?
- Does the desktop layout avoid excessive empty space?
- Are forms constrained to readable widths?
- Does navigation change appropriately?
- Does the application work in landscape mode?
- Does the application work in split-screen mode?
- Does Flutter Web work when the browser is resized?
- Are desktop mouse and keyboard interactions supported?
- Are mobile touch interactions comfortable?
- Does content avoid system cutouts?
- Is important application state preserved?
- Are device-type checks avoided for layout decisions?
- Are layouts tested at different widths?
45. Interview Questions
Q1. What is adaptive UI in Flutter?
Answer: Adaptive UI is a design approach where the application changes its layout or interaction pattern to remain usable in different available spaces and environments.
Q2. What is the difference between mobile and desktop UI?
Answer: Mobile UI generally prioritizes compact layouts and touch interaction, while desktop UI can use larger layouts, sidebars, multiple panels, mouse interaction, hover states and keyboard shortcuts.
Q3. Which widget is commonly used for responsive layout decisions?
Answer: LayoutBuilder is commonly used because it provides the constraints available from its parent.
Q4. What is MediaQuery.sizeOf?
Answer: It returns the current size of the application's window/view in logical pixels.
Q5. Why should device type not be the primary layout decision?
Answer: A device's physical screen size does not necessarily equal the space currently available to the application because of split-screen, resizable windows, desktop applications and web browsers.
Q6. How can a mobile navigation layout be changed for desktop?
Answer: The application can use a NavigationBar on compact layouts and a NavigationRail or sidebar on wider layouts.
Q7. Why use GridView on large screens?
Answer: GridView can distribute content across multiple columns and prevent individual items from becoming unnecessarily wide.
Q8. Why use ConstrainedBox?
Answer: It can limit maximum content width so that text, forms and other content remain readable on large screens.
Q9. What input methods should desktop UI support?
Answer: Mouse, trackpad, keyboard, scrolling, hover and keyboard navigation should be considered where appropriate.
Q10. Should mobile, tablet and desktop use completely separate applications?
Answer: No. Flutter allows you to share data, business logic and reusable components while changing the presentation structure according to available space.
46. Practice Exercises
- Create a Flutter application with separate mobile, tablet and desktop layouts.
- Create a responsive navigation system using NavigationBar and NavigationRail.
- Create a dashboard that changes from ListView to GridView.
- Create a product catalog with an adaptive grid.
- Create a responsive login screen with a maximum form width.
- Create a list-detail contact application.
- Create a desktop sidebar that disappears on compact layouts.
- Add hover effects for desktop users.
- Add keyboard-friendly navigation to a custom widget.
- Run the application in Chrome and resize the browser window to test the layouts.
47. Quick Revision Table
| Topic |
Key Point |
| Mobile UI |
Compact, touch-friendly and generally single-column. |
| Tablet UI |
Uses additional space for grids and side-by-side content. |
| Desktop UI |
Supports large layouts, sidebars and multiple panels. |
| LayoutBuilder |
Provides local parent constraints. |
| MediaQuery.sizeOf |
Provides application window/view size. |
| NavigationBar |
Useful for compact navigation. |
| NavigationRail |
Useful for wider layouts. |
| GridView |
Useful for multi-column content. |
| Expanded |
Uses available Flex space. |
| Flexible |
Allows flexible sizing within Flex layouts. |
| ConstrainedBox |
Controls maximum or minimum dimensions. |
| SafeArea |
Protects content from system UI and display cutouts. |
48. Official Flutter Resources
49. Flutter Training Resources
For structured Flutter training and practical learning resources, visit:
JustAcademy Flutter Training Course
To register for a Flutter course demo:
Register for Flutter Course Demo
50. Conclusion
Building Mobile, Tablet and Desktop UI in Flutter requires more than resizing widgets. A good multi-device application considers the available space, navigation requirements, content density, input methods, accessibility, and the strengths of each form factor.
Use LayoutBuilder when a component needs to respond to its local constraints and MediaQuery.sizeOf when the overall application window size matters. Combine these tools with Row, Column, Expanded, Flexible, GridView, NavigationBar, NavigationRail, ConstrainedBox and SafeArea to create layouts that remain useful across mobile, tablet, desktop and web environments.
The most important principle is: design for the space your application actually receives, not merely for the physical device category.